package com.wing.aspect.idempotent;

import cn.hutool.core.util.ObjectUtil;
import com.wing.common.enums.Idempotent;
import com.wing.common.exception.BusinessException;
import com.wing.common.utils.RequestKeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Author: zcg
 * @Description: 幂等切面实现
 * @Date: 2024/3/12
 **/
@Aspect
@Configuration
@Order(2)
@Slf4j
public class IdempotentAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;


    @Autowired
    public IdempotentAspect(StringRedisTemplate redissonClient) {
        this.redisTemplate = redissonClient;
    }

    @Around("execution(* com.wing.*.controller..*(..))) && @annotation(Idempotent)")
    public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Idempotent idempotent = method.getAnnotation(Idempotent.class);
        if (ObjectUtil.isNotEmpty(idempotent)) {
            if (StringUtils.isEmpty(idempotent.keyPrefix())) {
                throw new BusinessException("重复提交前缀不能为空");
            }
            //获取自定义key
            final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
            // 使用Redisson分布式锁的方式判断是否重复提交
            Boolean hasKey = redisTemplate.hasKey(lockKey);
            if (Boolean.TRUE.equals(hasKey)) {
                log.warn("请勿重复点击, key: {}", lockKey);
                throw new BusinessException("请勿重复点击！");
            }
            redisTemplate.opsForValue().set(lockKey, "1", idempotent.timeout(), TimeUnit.SECONDS);
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                throw new BusinessException(e.getMessage());
            } finally {
                redisTemplate.delete(String.valueOf(hasKey));
            }
        }

        return joinPoint.proceed();
    }
}


